﻿namespace LizardChess
open System
open System.IO
open System.Runtime.Serialization.Formatters.Binary
open System.Runtime.Serialization
module Eng =
    //STORAGE elements
    //set up paths
    let efold = 
        let ans = Path.Combine(Ref.basefold,"Engine")
        Directory.CreateDirectory(ans)|>ignore
        ans
    let logfile = Path.Combine(efold,"LogFile.txt")
    let setfile = Path.Combine(efold,"Settings.txt")
    let linestore = Path.Combine(efold,"LineStore2.txt")
    ///loadSettings - get stored setting
    let loadSettings() = 
        if not (File.Exists setfile) then File.WriteAllLines(setfile,[|"Houdini_15a_x64";"12";"20";"T" |])
        let lines = File.ReadAllLines(setfile)
        {engine=lines.[0];mindepth=(int)lines.[1];maxdepth=(int)lines.[2];log=(if lines.[3]="T" then true else false)}
    ///saveSettings - save settings to file
    let saveSettings(arr) = File.WriteAllLines(setfile,arr)
    ///engines - gets array of available engines
    let engines() = 
        Directory.GetFiles(efold,"*.exe")
        |> Array.map Path.GetFileNameWithoutExtension
    let engineso() = engines()|>Array.map (fun s -> s :> obj) 
    ///enginepath - gets engine from setting with full path
    let enginepath(set:eng) = Path.Combine(efold,set.engine)
    ///let loadLineStore - loads dictionary of line analysis results
    let loadLineStore() = 
        let ans = new System.Collections.Generic.Dictionary<string,string>()
        if (File.Exists linestore) then 
            let lines = File.ReadAllLines(linestore)
            let proc (ln:string) =
                let bits = ln.Split([|'#'|])
                ans.[bits.[0]] <- bits.[1]
            lines |> Array.iter proc
        ans
    ///let saveLineStore - saves dictionary of line analysis results
    let saveLineStore(ls:System.Collections.Generic.Dictionary<string,string>) = 
        let dat = ls |> Seq.map (fun (KeyValue(k,v)) -> k+"#"+v) |> Seq.toArray 
        File.WriteAllLines(linestore,dat)
    /// empanl - empty enganl
    let empanl = {depth=0;scr=0;bestmv="";resp="";bmPGN="";rmPGN=""}
    /// getanl - gets engine analysis given string of moves
    let getanl (ls:System.Collections.Generic.Dictionary<string,string>) k =
        let p,v = ls.TryGetValue k
        if p then
            let bits = v.Split([|','|])
            {depth=int(bits.[0]);scr=int(bits.[1]);bestmv=bits.[2];resp=bits.[3];bmPGN=bits.[4];rmPGN=bits.[5]}
        else
            empanl
    /// getanls - gets engine analyses given varn
    let getanls (ls:System.Collections.Generic.Dictionary<string,string>,(vn:varn)) =
        let strs = Varn.cur2txt vn
        strs |> Array.map (getanl ls)
 
    /// dpth - gets dpth from string from line store
    let dpth (v:string) =
        let bits = v.Split([|','|])
        int(bits.[0])
    
    /// alreadyDone - confirms whether already in line store
    let alreadyDone(ls:System.Collections.Generic.Dictionary<string,string>,k,d) = 
        let p,curv = ls.TryGetValue k
        (p && (dpth curv) >= d)
    /// addLineStore - adds to dictionary of line analysis results
    let addLineStore(ls:System.Collections.Generic.Dictionary<string,string>,k,v) = 
        if not (alreadyDone(ls,k,dpth v)) then ls.[k] <- v
     
    //Utility with array of mvs
    ///let mvs2str - converts an array of mvs to string given a length
    let mvs2str (mvs:string[], ct) = Array.reduce (fun a b -> a+" "+b) mvs.[0..ct-1] 
    ///let str2mvs - converts string to an array of mvs given a length
    let str2mvs (str:string, ct) = 
        let arr = str.Split([|' '|])
        arr.[0..ct-1]
    ///let str2str - converts string to a string given a length
    let str2str (str:string, ct) = 
        mvs2str(str2mvs(str,ct),ct)
    ///let str2psn - converts string to a psn
    let str2psn (str:string) = 
        let strmvsl = str.Split([|' '|]) |> List.ofArray
        let rec genpsn (psn:posn) strl =
            if strl=[] then psn 
            else
                let mv = Posn.FndMv(strl.Head, psn)
                let opsn = Posn.DoMove(mv, psn)
                genpsn opsn strl.Tail
        genpsn (Posn.st()) strmvsl

    ///let procbm - processes best move information from answer from engine and stores result
    let procbm(ls:System.Collections.Generic.Dictionary<string,string>,ln,mvct,answer:string,scrlin:string,scrlin1:string) =
        let getitem (lin:string) lbl =
            let rec getit (lst:string list) = 
                let hd = lst.Head
                if lst.Tail=[] then ""
                elif hd = lbl then lst.Tail.Head
                else getit lst.Tail
            lin.Split([|' '|]) |> List.ofArray |> getit
        if answer.Length>3 then
            let scr = getitem scrlin "cp"
            let depth = getitem scrlin "depth" 
            let resp = getitem scrlin answer
            if resp.Length>3 then
                let strmvs = str2str(ln,mvct)
                let psn = str2psn strmvs
                if scr="" || depth="" then
                    let scr1 = getitem scrlin1 "cp"
                    let depth1 = getitem scrlin1 "depth" 
                    let resp1 = getitem scrlin1 answer
                    let bm = Posn.FndMv(answer, psn)
                    let psn1 = Posn.DoMove(bm, psn)
                    let bmPgn = psn1.Mhst.[0].PGN
                    let rm = Posn.FndMv(resp1, psn1)
                    let psn2 = Posn.DoMove(rm, psn1)
                    let rmPgn = psn2.Mhst.[0].PGN
                    if not (scr1="" || depth1="") then 
                        addLineStore(ls, strmvs, depth1 + "," + scr1 + "," + answer + "," + resp1 + "," + bmPgn + "," + rmPgn)
                else
                    let bm = Posn.FndMv(answer, psn)
                    let psn1 = Posn.DoMove(bm, psn)
                    let bmPgn = psn1.Mhst.[0].PGN
                    let rm = Posn.FndMv(resp, psn1)
                    let psn2 = Posn.DoMove(rm, psn1)
                    let rmPgn = psn2.Mhst.[0].PGN
                    addLineStore(ls, strmvs, depth + "," + scr + "," + answer + "," + resp + "," + bmPgn + "," + rmPgn)

    //general engine analysis for a variation
    let rec getbms (bms:engbm list) len bw num cpsn (mvl:move list) (str:string) (ls:System.Collections.Generic.Dictionary<string,string>) =
        if len >= str.Length-5 then List.rev bms
        else
            let curstr = str.Substring(0,len)
            let curmv = str.Substring(len+1,4)
            let anl = getanl ls curstr
            let nlen = len+5
            let nbw = if bw="B" then "W" else "B"
            let nnum = if bw="B" then num+1 else num
            // need to make a move and get the PGN
            let bmPgn = anl.bmPGN
            let bscr = anl.scr
            let opsn = Posn.DoMove(mvl.Head,cpsn)
            if anl.bestmv = curmv || anl.bestmv = "" then getbms bms nlen nbw nnum opsn mvl.Tail str ls else getbms ({bnum=num;bisw=(if bw="W" then true else false);bPGN=bmPgn;bstr=(bw+num.ToString()+"-"+bmPgn);bscr=bscr}::bms) nlen nbw nnum opsn mvl.Tail str ls
    let strmvl2bms (ls:System.Collections.Generic.Dictionary<string,string>) (str:string) (vnpsn:posn) = 
        let mvl = List.rev vnpsn.Mhst
        let opsn = Posn.DoMove(mvl.Head,Posn.st())
        let lbms = getbms [] 4 "B" 1 opsn mvl.Tail str ls
        lbms |> List.toArray        

     /// extlin - extends line given varn, enganls and selvar
    let extlin ((vn:varn),(ea:enganl[]),(s:int)) =
        let barr = vn.brchs |> List.toArray
        let selb = barr.[s]
        let sele = ea.[s]
        if not (sele.bestmv=""||sele.resp="") then
            let bm = Posn.FndMv(sele.bestmv,selb)
            let p1 = Posn.DoMove(bm,selb)
            let rm = Posn.FndMv(sele.resp,p1)
            let p2 = Posn.DoMove(rm,p1)
            barr.[s] <- p2
        {vn with brchs=List.ofArray(barr)}

    /// extall - extends all lines given varn, enganls
    let extall ((vn:varn),(ea:enganl[])) =
        let barr = vn.brchs |> List.toArray
        for s = 0 to barr.Length-1 do
            let selb = barr.[s]
            let sele = ea.[s]
            if not (sele.bestmv=""||sele.resp="") then
                let bm = Posn.FndMv(sele.bestmv,selb)
                let p1 = Posn.DoMove(bm,selb)
                let rm = Posn.FndMv(sele.resp,p1)
                let p2 = Posn.DoMove(rm,p1)
                barr.[s] <- p2
        {vn with brchs=List.ofArray(barr)}


